#include <iostream>
#include <thread>
#include <mutex>
#include <ctime>
#include <array>

// modify the number of philosophers
#define PHILOSOPHERS 10

using namespace std;
using namespace std::this_thread;
using namespace std::chrono;

mutex chopstick[PHILOSOPHERS];

int meal_time[PHILOSOPHERS];

// initialize the meal time
void init()
{
	for (int i = 0; i < PHILOSOPHERS; ++i)
		meal_time[i] = 50;
}

void eat(int id)
{
	while (true)
	{
		// try to get the first chopstick
		if (chopstick[id].try_lock())
		{
			// try to get the second chopstick
			if (chopstick[(id + 1) % PHILOSOPHERS].try_lock())
			{
				cout << "Philosopher " << id + 1 << " begin eating ..." << endl;

				int eated_time = rand() % (30 + 1);

				// eating ...
				sleep_for(seconds(eated_time % 10));

				meal_time[id] -= eated_time;

				// finished eating
				if (meal_time[id] <= 0)
				{
					cout << "Philosopher " << id + 1 << " finished." << endl;
					chopstick[id].unlock();
					chopstick[(id + 1) % PHILOSOPHERS].unlock();
					break;
				}

				cout << "Philosopher " << id + 1 << " still need " << meal_time[id] << " minues to eat." << endl;
				chopstick[id].unlock();
				chopstick[(id + 1) % PHILOSOPHERS].unlock();

				// thinking ...
				sleep_for(milliseconds(200));
			}
			else
			{
				//if unable to get the second chopstick,then give away the first one.
				chopstick[id].unlock();
				//thinking...
				sleep_for(milliseconds(100));
			}
		}
	}
}

int main()
{
	//initialize each Philosopher's meal time.
	init();

	array<thread, PHILOSOPHERS> threads;

	cout << "Starting..." << endl;
	for (int i = 0; i < PHILOSOPHERS; ++i)
		threads[i] = thread(eat, i);

	for (int i = 0; i < PHILOSOPHERS; ++i)
		threads[i].join();

	printf("All philosophers done.\n");
	return 0;
}